using System;
using System.Collections.Generic;
using System.Text;

namespace Web.Ajax
{
    public class Encryption
    {
        #region base64chars
        public static readonly char[] base64chars = new char[64]
		  {  
			'A','B','C','D','E','F','G','H','I','J','K','L','M','N',
			'O','P','Q','R','S','T','U','V','W','X','Y', 'Z',
			'a','b','c','d','e','f','g','h','i','j','k','l','m','n',
			'o','p','q','r','s','t','u','v','w','x','y','z',
			'0','1','2','3','4','5','6','7','8','9','-','_'
		  }; 
        #endregion

        #region Xor
        public static string Xor(string s)
        {
            string r = "";
            int XorKey = Configuration.Settings.Current.XorKey;
            for (int i = 0; i < s.Length; i++)
            {
                r += (char)(((int)s[i]) ^ XorKey);
            }
            return r;
        } 
        #endregion

        #region sixbit2char
        private static char sixbit2char(byte b)
        {

            if ((b >= 0) && (b <= 63))
            {
                return Encryption.base64chars[(int)b];
            }
            else
            {
                return ' ';
            }
        } 
        #endregion

        #region Base64Encode
        public static string Base64Encode(string s)
        {
            byte[] source = System.Text.ASCIIEncoding.ASCII.GetBytes(s);
            int length, length2;
            int blockCount;
            int paddingCount;

            length = source.Length;
            if ((length % 3) == 0)
            {
                paddingCount = 0;
                blockCount = length / 3;
            }
            else
            {
                paddingCount = 3 - (length % 3);//need to add padding

                blockCount = (length + paddingCount) / 3;
            }
            length2 = length + paddingCount;//or blockCount *3

            byte[] source2;
            source2 = new byte[length2];
            //copy data over insert padding

            for (int x = 0; x < length2; x++)
            {
                if (x < length)
                {
                    source2[x] = source[x];
                }
                else
                {
                    source2[x] = 0;
                }
            }

            byte b1, b2, b3;
            byte temp, temp1, temp2, temp3, temp4;
            byte[] buffer = new byte[blockCount * 4];
            int resultLength = blockCount * 4 + paddingCount;
            char[] result = new char[resultLength];
            for (int x = 0; x < blockCount; x++)
            {
                b1 = source2[x * 3];
                b2 = source2[x * 3 + 1];
                b3 = source2[x * 3 + 2];
                temp1 = (byte)((b1 & 252) >> 2);//first
                temp = (byte)((b1 & 3) << 4);
                temp2 = (byte)((b2 & 240) >> 4);
                temp2 += temp; //second
                temp = (byte)((b2 & 15) << 2);
                temp3 = (byte)((b3 & 192) >> 6);
                temp3 += temp; //third
                temp4 = (byte)(b3 & 63); //fourth
                buffer[x * 4] = temp1;
                buffer[x * 4 + 1] = temp2;
                buffer[x * 4 + 2] = temp3;
                buffer[x * 4 + 3] = temp4;
            }

            for (int x = 0; x < blockCount * 4; x++)
                result[x] = sixbit2char(buffer[x]);

            //covert last "A"s to "=", based on paddingCount

            switch (paddingCount)
            {
                case 1: 
                    result[resultLength-1] = '='; 
                    break;
                case 2:
                    result[resultLength - 1] = '=';
                    result[resultLength - 2] = '=';
                    break;
                default: 
                    break;
            }
            return new string(result);
        } 
        #endregion

        #region char2sixbit
        private static byte char2sixbit(char c)
        {

            if (c == '=')
                return 0;
            else
            {
                for (int x = 0; x < 64; x++)
                {
                    if (Encryption.base64chars[x] == c)
                        return (byte)x;
                }
                //should not reach here
                return 0;
            }

        } 
        #endregion

        #region Base64Decode
        public static string Base64Decode(string s)
        {
            char[] source = s.ToCharArray();
            int length, length2, length3;
            int blockCount;
            int paddingCount;

            int temp = 0;
            length = source.Length;

            //find how many padding are there

            for (int x = 0; x < 2; x++)
            {
                if (source[length - x - 1] == '=')
                    temp++;
            }
            paddingCount = temp;
            //calculate the blockCount;

            //assuming all whitespace and carriage returns/newline were removed.

            blockCount = length / 4;
            length2 = blockCount * 3;

            byte[] buffer = new byte[length];//first conversion result

            byte[] buffer2 = new byte[length2];//decoded array with padding


            for (int x = 0; x < length; x++)
            {
                buffer[x] = char2sixbit(source[x]);
            }

            byte b, b1, b2, b3;
            byte temp1, temp2, temp3, temp4;

            for (int x = 0; x < blockCount; x++)
            {
                temp1 = buffer[x * 4];
                temp2 = buffer[x * 4 + 1];
                temp3 = buffer[x * 4 + 2];
                temp4 = buffer[x * 4 + 3];

                b = (byte)(temp1 << 2);
                b1 = (byte)((temp2 & 48) >> 4);
                b1 += b;

                b = (byte)((temp2 & 15) << 4);
                b2 = (byte)((temp3 & 60) >> 2);
                b2 += b;

                b = (byte)((temp3 & 3) << 6);
                b3 = temp4;
                b3 += b;

                buffer2[x * 3] = b1;
                buffer2[x * 3 + 1] = b2;
                buffer2[x * 3 + 2] = b3;
            }
            //remove paddings

            length3 = length2 - paddingCount;
            byte[] result = new byte[length3];

            for (int x = 0; x < length3; x++)
            {
                result[x] = buffer2[x];
            }

            return System.Text.ASCIIEncoding.ASCII.GetString(result);
        } 
        #endregion

        public static string Encrypt(string s)
        {
            s = Xor(s);
            s = Base64Encode(s);
            return s;
        }

        public static string Decrypt(string s)
        {
            s = Base64Decode(s);
            s = Xor(s);
            return s;
        }
    }
    
}
